package io.gitee.zhangbinhub.admin.resource.server.tools

import com.fasterxml.jackson.databind.ObjectMapper
import io.gitee.zhangbinhub.acp.boot.log.LogAdapter
import io.gitee.zhangbinhub.acp.cloud.resource.server.constant.AcpCloudResourceServerConstant
import io.gitee.zhangbinhub.admin.resource.server.vo.TokenUserInfoVo
import org.bouncycastle.util.encoders.Base64
import org.springframework.security.oauth2.core.OAuth2AuthenticationException
import org.springframework.security.oauth2.core.OAuth2AuthorizationException
import org.springframework.security.oauth2.core.OAuth2Error
import org.springframework.security.oauth2.core.OAuth2ErrorCodes
import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthentication
import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionAuthenticatedPrincipal

class TokenTools(
    private val logAdapter: LogAdapter,
    private val objectMapper: ObjectMapper
) {
    @Throws(OAuth2AuthenticationException::class)
    fun encryptUserInfo(userInfoVo: TokenUserInfoVo): String = try {
        Base64.toBase64String(objectMapper.writeValueAsBytes(userInfoVo))
    } catch (e: Exception) {
        throw OAuth2AuthenticationException(OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR, e.message, null), e)
    }

    @Throws(OAuth2AuthorizationException::class)
    fun decryptUserInfo(ciphertext: String): TokenUserInfoVo = try {
        objectMapper.readValue(Base64.decode(ciphertext), TokenUserInfoVo::class.java)
    } catch (e: Exception) {
        throw OAuth2AuthorizationException(OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN, e.message, null), e)
    }

    @Throws(OAuth2AuthorizationException::class)
    fun getAuthenticatedPrincipal(bearerTokenAuthentication: BearerTokenAuthentication): OAuth2IntrospectionAuthenticatedPrincipal =
        bearerTokenAuthentication.principal as? OAuth2IntrospectionAuthenticatedPrincipal
            ?: throw OAuth2AuthorizationException(OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN, "invalid token", null))

    @Throws(OAuth2AuthorizationException::class)
    fun getUserInfoFromToken(bearerTokenAuthentication: BearerTokenAuthentication): TokenUserInfoVo =
        (getAuthenticatedPrincipal(bearerTokenAuthentication).getClaim(
            AcpCloudResourceServerConstant.TOKEN_CLAIMS_USER_INFO
        ) as? String)?.let { claimValue ->
            try {
                decryptUserInfo(claimValue)
            } catch (e: Exception) {
                logAdapter.error(e.message, e)
                TokenUserInfoVo()
            }
        } ?: TokenUserInfoVo()
}